package net.csdn.business.mirror.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import net.csdn.business.common.domain.mirror.GithubSourceDto;
import net.csdn.business.common.enums.ResultCodeEnum;
import net.csdn.business.common.exception.BusinessException;
import net.csdn.business.common.service.SuperServiceImpl;
import net.csdn.business.mirror.common.Constants;
import net.csdn.business.mirror.entity.bean.GithubToken;
import net.csdn.business.mirror.entity.bean.ScheduleTask;
import net.csdn.business.mirror.entity.bean.UserGithubToken;
import net.csdn.business.mirror.enums.ScheduleTaskEnum;
import net.csdn.business.mirror.mapper.ScheduleTaskMapper;
import net.csdn.business.mirror.service.GithubTokenService;
import net.csdn.business.mirror.service.ScheduleTaskService;
import net.csdn.business.mirror.utils.TimeUtil;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ScheduleTaskServiceImpl extends SuperServiceImpl<ScheduleTaskMapper, ScheduleTask> implements ScheduleTaskService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private GithubTokenService githubTokenService;

    @Override
    public ScheduleTask saveTask(ScheduleTask scheduleTask) {
        baseMapper.insert(scheduleTask);
        return scheduleTask;
    }

    public ScheduleTask getLastByName(String name) {
        if (StringUtils.isEmpty(name)) {
            return null;
        }
        QueryWrapper<ScheduleTask> queryWrapper = new QueryWrapper<>();
        LambdaQueryWrapper<ScheduleTask> lambdaQueryWrapper = queryWrapper.lambda();
        lambdaQueryWrapper.eq(ScheduleTask::getStatus, ScheduleTaskEnum.Status._1.code);
        lambdaQueryWrapper.eq(ScheduleTask::getName, name);
        queryWrapper.orderBy(true, false, "updated_at");
        Page<ScheduleTask> page = new Page<>(1, 1, false);
        baseMapper.selectPage(page, lambdaQueryWrapper);
        if (CollectionUtils.isEmpty(page.getRecords())) {
            return null;
        }
        return page.getRecords().get(0);
    }

    @Override
    public List<ScheduleTask> validList() {
        QueryWrapper<ScheduleTask> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(ScheduleTask::getStatus, ScheduleTaskEnum.Status._1.code);
        wrapper.lambda().orderByDesc(ScheduleTask::getUpdatedAt);
        return baseMapper.selectList(wrapper);
    }

    @Override
    public GithubSourceDto getGithubSource() {
        QueryWrapper<ScheduleTask> wrapper = new QueryWrapper<>();
        wrapper.lambda().in(ScheduleTask::getName, ScheduleTaskEnum.Name.SYNC_GITHUB.getType(), ScheduleTaskEnum.Name.MIRROR.getType());
        wrapper.lambda().orderByDesc(ScheduleTask::getUpdatedAt);
        List<ScheduleTask> tasks = baseMapper.selectList(wrapper);
        if (CollectionUtils.isEmpty(tasks)) {
            return null;
        }
        ScheduleTask mirrorTask = tasks.stream().filter(item -> item.getName().equals(ScheduleTaskEnum.Name.MIRROR.getType())).findFirst().orElse(null);
        if (Objects.isNull(mirrorTask)) {
            return null;
        }
        ScheduleTask syncTask = tasks.stream().filter(item -> item.getName().equals(ScheduleTaskEnum.Name.SYNC_GITHUB.getType())).findFirst().orElse(null);
        List<GithubToken> tokens = githubTokenService.getAllToken();
        GithubSourceDto result = new GithubSourceDto();
        result.setCron(mirrorTask.getCron());
        result.setContainBaseInfo(Objects.nonNull(syncTask) && syncTask.getStatus().equals(ScheduleTaskEnum.Status._1.code));
        result.setStatus(mirrorTask.getStatus());
        result.setGithubTokens(tokens.stream().map(GithubToken::getToken).collect(Collectors.toList()));
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveGithubSource(GithubSourceDto sourceDto) {
        try {
            TimeUtil.getCronDuration(sourceDto.getCron());
        } catch (Exception e) {
            throw new BusinessException(ResultCodeEnum.PARAMETER_ERROR_CUSTOM, "cron");
        }
        //先删除所有token
        githubTokenService.deleteAllToken();
        //然后保存所有新的token
        List<String> githubTokens=sourceDto.getGithubTokens();
        if (!CollectionUtils.isEmpty(githubTokens)) {
            githubTokenService.saveToken(githubTokens);
        }
        //先删除相关定时任务
        QueryWrapper<ScheduleTask> wrapper = new QueryWrapper<>();
        wrapper.lambda().in(ScheduleTask::getName, ScheduleTaskEnum.Name.SYNC_GITHUB.getType(), ScheduleTaskEnum.Name.MIRROR.getType());
        baseMapper.delete(wrapper);
        // 再插入新的定时任务
        ScheduleTask mirrorTask = ScheduleTask.builder()
                .cron(sourceDto.getCron())
                .status(sourceDto.getStatus())
                .name(ScheduleTaskEnum.Name.MIRROR.getType())
                .title(ScheduleTaskEnum.Name.MIRROR.getTitle())
                .build();
        baseMapper.insert(mirrorTask);
        if (BooleanUtils.isTrue(sourceDto.getContainBaseInfo())) {
            ScheduleTask syncTask = ScheduleTask.builder()
                    .cron(sourceDto.getCron())
                    .status(sourceDto.getStatus())
                    .name(ScheduleTaskEnum.Name.SYNC_GITHUB.getType())
                    .title(ScheduleTaskEnum.Name.SYNC_GITHUB.getTitle())
                    .build();
            baseMapper.insert(syncTask);
        }
        rebuild();
    }

    @Override
    public void rebuild() {
        stringRedisTemplate.convertAndSend(Constants.LOCAL_CACHE_UPDATE_CHANNEL, Constants.LOCAL_CACHE_UPDATE_MSG);
    }

}